/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.


See LICENSE and AUTHORS for more information.
*/
#include "coreserver.h"
#include <QCoreApplication>
#include <QStringList>
#include <QSettings>
#include <QFileInfo>
#include <QTime>
#include "corexmlparser.h"
#include "corexmlgenerator.h"
#include <stdlib.h>

CoreServer::CoreServer(QString keyfile,QString certfile)
    : QTcpServer()
{
    listen( QHostAddress::Any, 2342 );
    qDebug() << "Now listening on Port 2342 ...";

    keyFile = keyfile;
    certFile = certfile;
}


/**
  Called whenever a client connects to the server-socket.
  This is used to setup a new socket and start SSL/TLS
  encryption
  */
void CoreServer::incomingConnection( int socketDescriptor )
{
    QSslSocket *sslSocket = new QSslSocket( this );

    connect( sslSocket, SIGNAL(readyRead()), this, SLOT(readyRead()) );
    connect( sslSocket, SIGNAL(encrypted()), this, SLOT(encrypted()) );
    connect( sslSocket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(sslModeChanged(QSslSocket::SslMode)) );
    connect( sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)), this, SLOT(sslErrors(const QList<QSslError>&)) );
    connect( sslSocket, SIGNAL(disconnected()), this, SLOT(disconnected()) );
    connect( sslSocket, SIGNAL(connected()), this, SLOT(connected()) );
    connect( sslSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)) );
    connect( sslSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(stateChanged(QAbstractSocket::SocketState)) );

    QString mykey ="";
    QFile keyfile(keyFile);
    if (!keyfile.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qDebug() << "file could not be opened: " << keyfile.errorString();
    }
    while(!keyfile.atEnd()) {
        mykey.append(keyfile.readLine());
    }

    QString mycert="";
    QFile certfile(certFile);
    certfile.open(QIODevice::ReadOnly | QIODevice::Text);
    while(!certfile.atEnd()) {
	mycert.append(certfile.readLine());
    }

    sslSocket->setLocalCertificate( QSslCertificate( mycert.toAscii() ) );
    sslSocket->setPrivateKey( QSslKey( mykey.toAscii(), QSsl::Rsa ) );

    if( sslSocket->setSocketDescriptor( socketDescriptor ) )
    {
        qDebug() << "  sslSocket mode: " << (int)sslSocket->mode();
        qDebug() << "  sslSocket state:" << (int)sslSocket->state();

        qDebug() << "Calling sslSocket->startServerEncryption()";
        sslSocket->startServerEncryption();
    }
    else
    {
        qWarning() << "Couldn't setSocketDescriptor(" << socketDescriptor << ") for this connection";
        delete sslSocket;
    }
}

/**
* Connection has been encrypted using SSL
*
* We start transmitting data now, starting with
* protocol information, thus requesting the
* client to authentiate.
*/
void CoreServer::encrypted()
{
    qDebug() << "encrypted";
    QSslSocket *socket = qobject_cast<QSslSocket*>( sender() );
    Q_ASSERT( socket );

    /* generate a unique id for this connection */
    QString id = QTime::currentTime().toString("hh:mm:ss.zzz");
    srand(QTime::currentTime().msec());
    id += QString(rand());

    m_sockets.insert(id,socket);

    /* create XML utils */
    CoreXMLGenerator* gen =  getXMLGenerator(id);

    // we dont use the parser, but want it to be created
    getXMLParser(id);


    emit connectXMLTools(id);

    gen->generateProtocolVersion();
}

QString CoreServer::getUsername(QString id)
{
    return m_usernames.key(id,"");
}

QList<QString> CoreServer::getIds(QString username)
{
   return m_usernames.values(username);
}

QString CoreServer::getId(CoreXMLParser* parser)
{
    return m_parsers.key(parser,"");
}


void CoreServer::sslModeChanged( QSslSocket::SslMode mode )
{
    qDebug() << "CoreServer::sslModeChanged(" << (int)mode << ")";
}

void CoreServer::sslErrors( const QList<QSslError> &errList )
{
    foreach( QSslError err, errList )
        qDebug() << "            " << err.errorString();
}

void CoreServer::disconnected()
{
    qDebug() << "CoreServer::disconnected()";
}

void CoreServer::connected()
{
    qDebug() << "CoreServer::connected()";
}

void CoreServer::error( QAbstractSocket::SocketError err )
{
    QSslSocket *socket = qobject_cast<QSslSocket*>( sender() );
    Q_ASSERT( socket );

    qDebug() << "CoreServer::error(" << (int)err << socket->errorString() << ")";
}

void CoreServer::stateChanged( QAbstractSocket::SocketState state )
{
    qDebug() << "CoreServer::stateChanged(" << (int)state << ")";
}

void CoreServer::readyRead()
{
    QSslSocket *socket = qobject_cast<QSslSocket*>( sender() );
    Q_ASSERT( socket );

    getXMLParser(m_sockets.key(socket,""))->parseContinue();
}

void CoreServer::send(QString id, QString data) {
    QSslSocket *socket = m_sockets.value(id,NULL);
    if(socket) {
        socket->write(data.toUtf8()+"\r\n");
    }
    else {
        // if client disconnected in the mean time
        // and the controller did not yet notice.
        emit clientDisposed(id);
    }
}

void CoreServer::errorMessage(int errnum,QString errstr)
{
    qDebug() << "error num="<<errnum<<" occured, saying: "<<errstr;
}


CoreXMLGenerator* CoreServer::getXMLGenerator(QString id) {
    CoreXMLGenerator* gen = m_generators.value(id,NULL);
    if(!gen)
    {
        gen = new CoreXMLGenerator(m_sockets.value(id));
        m_generators.insert(id,gen);
    }
    return gen;
}

CoreXMLParser* CoreServer::getXMLParser(QString id) {
    CoreXMLParser* parser = m_parsers.value(id,NULL);
    if(!parser) {
        parser = new CoreXMLParser(m_sockets.value(id));
        m_parsers.insert(id,parser);
    }
    return parser;
}
